#include "Lily_help.h"
#include <math.h>
#include "hashlib.h"

int32_t search_fun_in_Lily_ui(const char *item)
{
	return search_obj(lily_ui.funs, item);
}

int32_t public_a_fun_link_n(const char *name, void *link, char n)
{
	Fun_t fun = {(char *)name, (HASH)0, link, n, 'f'};
	return public_obj(lily_ui.funs, &fun);
}
int32_t public_a_fun_link_int(const char *name, void *link, char n)
{
	Fun_t fun = {(char *)name, (HASH)0, link, n, 'd'};
	return public_obj(lily_ui.funs, &fun);
}
int32_t remove_fun(int32_t index)
{
	return list_remove_at(lily_ui.funs, index);
}
int32_t remove_fun_by_str(char *cmd)
{
	int32_t index = search_fun_in_Lily_ui(cmd);
	if (index < 0)
		return -1;
	return remove_fun(index);
}
int32_t shell_do_fun(char* rx)
{
	int32_t hit1, at = 0;
	hit1 = str_contains(rx, '=');
	Var assign_field = NULL;
	if (hit1)
	{
		rx[hit1 - 1] = '\0';
		int32_t n = search_var_in_Lily_ui(rx);
		if (n < 0)
		{
			return 0;
		}
		assign_field = li_vars + n;
		at = _ebrace_value_from_string(rx);
		if (at < -1)
		{
			li_error("bad index", -__LINE__);
		}
		rx += hit1;
	}
	int32_t hit = search_fun_in_Lily_ui(rx);
	if (hit < 0)
	{
		return 0;
	}
	Fun fun = &li_funs[hit];
	void* f = fun->ref;
	typedef float (*f0)();
	typedef float (*f1)(float);
	typedef float (*f2)(float, float);
	typedef float (*f3)(float, float, float);
	typedef float (*f4)(float, float, float, float);
	typedef float (*fi1)(int32_t);
	typedef float (*fi2)(int32_t, int32_t);
	typedef float (*fi3)(int32_t, int32_t, int32_t);
	typedef float (*fi4)(int32_t, int32_t, int32_t, int32_t);
	float result;
	if (fun->narg == 0)
	{
		result = ((f0)f)();
		if (assign_field != NULL)
		{
			if (at == -1)
			{
				at = 0;
			}
			switch (assign_field->type)
			{
			case 'f':
				set_value_of_var(assign_field, at, to_voidf(result));
				break;
			case 'd':
			case 'c':
				set_value_of_var(assign_field, at, to_void((int32_t)result));
				break;
			default:
				assign_field->val.vf = result;
			}
		}
		return 1;
	}
	int32_t using_raw_mode = str_replace_by_str(rx, "(),", ' ');
	Li_List list = str_split(rx, ' ');
	hit = list->count;
	str* para = (char**)(list->content);
	float* nums = (float*)(list->content); // that's a in-place replacement
	int32_t error = 0;
	int32_t i;
	for (i = 1; i < hit; i++) //skip the first one
	{
		if (para[i][0] == '\"') // a string argument
		{
			para[i][strlen(para[i]) - 1] = '\0'; // delete the last "\""
			para[i]++;							 // skip the first "\""
			continue;
		}
		if (using_raw_mode)
		{
			int32_t ind = search_var_in_Lily_ui(para[i]);
			if (ind < 0)
			{
				error = -2;
				__li_error_msg = "var not found";
				break;
			}
			int32_t var_at = _ebrace_value_from_string(para[i]);
			if (var_at < -1)
			{
				error = -3;
				__li_error_msg = "bad index";
				break;
			}

			if (var_at == -1) // like a
			{
				nums[i] = li_vars[ind].val.vf;
			}
			else //like a[0]
				nums[i] = li_vars[ind].val.pf[var_at];

			continue;
		}
		nums[i] = _value_from_string_or_var(para[i]);
		if (isnan(nums[i]))
		{
			error = -1;
			__li_error_msg = "bad value";
			break;
		}
	}
	if (error != 0)
	{
		delete_list(list);
		return error;
	}

	int32_t narg = list->count - 1;
	nums += 1;			   // the fist para is like "fun", so the nums[0] is no meanings
	if (~(fun->narg) != 0) //
	{
		narg = fun->narg; // need narg paras
		if (list->count - 1 < narg)
		{
			static float nums_n[4];
			int32_t i;
			for (i = 0; i < list->count - 1; i++)
				nums_n[i] = nums[i];
			if (i == 0) //no para provided
			{
				nums_n[i++] = 0.0f;
			}
			for (; i < narg; i++)
				nums_n[i] = nums_n[i - 1]; // fill with the last number
			nums = nums_n;
		}
	}

	if (using_raw_mode || fun->type == 'f')
	{
		switch (narg)
		{
		case 0:
			result = ((f0)f)();
			break;
		case 1:
			result = ((f1)f)(nums[0]);
			break;
		case 2:
			result = ((f2)f)(nums[0], nums[1]);
			break;
		case 3:
			result = ((f3)f)(nums[0], nums[1], nums[2]);
			break;
		case 4:
			result = ((f4)f)(nums[0], nums[1], nums[2], nums[3]);
			break;
		default:
			error = -4;
			__li_error_msg = "too many paras(>5)";
			break;
		}
	}
	else if (fun->type == 'd') //int32_t
	{
		switch (narg)
		{
		case 0:
			result = ((f0)f)();
			break;
		case 1:
			result = ((fi1)f)(nums[0]);
			break;
		case 2:
			result = ((fi2)f)(nums[0], nums[1]);
			break;
		case 3:
			result = ((fi3)f)(nums[0], nums[1], nums[2]);
			break;
		case 4:
			result = ((fi4)f)(nums[0], nums[1], nums[2], nums[3]);
			break;
		default:
			error = -4;
			__li_error_msg = "too many paras(>5)";
			break;
		}
	}

	if (assign_field != NULL)
	{
		//assign_field->val.vf = result;
		if (using_raw_mode)
		{
			if (at == -1)
				assign_field->val.vf = result;
			else
			{
				assign_field->val.pf[at] = result;
			}
		}
		else
		{
			switch (assign_field->type)
			{
			case 'f':
				set_value_of_var(assign_field, at, to_voidf(result));
				break;
			case 'd':
			case 'c':
				set_value_of_var(assign_field, at, to_void((int32_t)result));
				break;
			default:
				assign_field->val.vf = result;
			}
		}
	}
	delete_list(list);
	if (error < 0)
	{
		return error;
	}
	return 1;
}
